from ctypes import *

WORD = c_ushort                         # 本来是 WORD 类型的 结果没找到 查了下 WORD 是 0 - 65535
                                        # short 是 -32768,32767 所以 unsigned short 也是 0 - 65535

DWORD = c_ulong                         # DWORD 是 Double Word， 每个word为2个字节的长度，DWORD 双字即为4个字节，每个字节是8位，共32位。
                                        # 因此选择 c语言 中的 long 类型 ulong 则表示没有负数

ULONG_PTR = POINTER(DWORD)              # ULONG_PTR 无符号 长整型 指针 ulong 取指针即可

LONG = c_long                           # C语言 中的 long

class MOUSEINPUT(Structure):
    _fields_ = [
        ('dx', LONG),                   # 鼠标点击 x 坐标
        ('dy', LONG),                   # 鼠标点击 y 坐标
        ('mouseData', DWORD),          # 鼠标左键 XBUTTON1【0×0001】 鼠标右键 XBUTTON2【0×0002】
        ('dwFlags', DWORD),             # 一组位标志，指定鼠标移动和按钮单击的各个方面
        ('time', DWORD),                # 点击间隔  默认建议为 50 ms
        ('dwExtraInfo', ULONG_PTR)      # 与鼠标事件关联的其他值 https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getmessageextrainfo
    ]
class KEYBDINPUT(Structure):
    _fields_ = [
        ('wVk', WORD),                  # 键盘码 如果dwFlags成员指定KEYEVENTF_UNICODE，则wVk必须为0
        ('wScan', WORD),                # 密钥的硬件扫描代码。如果dwFlags指定KEYEVENTF_UNICODE，则wScan指定要发送到前台应用程序的Unicode字符。
        ('dwFlags', DWORD),             # 指定键盘的各个方面。该成员可以是以下值的某些组合
        ('time', DWORD),                # 延迟
        ('dwExtraInfo', ULONG_PTR)      # 与键盘事件关联的其他值 https://msdn.microsoft.com/en-us/library/ms644937(v=VS.85).aspx
    ]
class HARDWAREINPUT(Structure):
    _fields_ = [
        ('uMsg', DWORD),                # 输入硬件生成的消息。
        ('wParamL', WORD),              # uMsg的lParam参数的低位字。
        ('wParamH', WORD)               # uMsg的lParam参数的高位字。
    ]

class Union(Union):                 # 继承 Union 定义一个 共有体 Union   # 由于 python 没有 MOUSEINPUT 这些变量类型，还需要继续定义
    _fields_ = [
        ('mi', MOUSEINPUT),         # 鼠标 https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-tagmouseinput
        ('ki', KEYBDINPUT),         # 键盘 https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-tagkeybdinput
        ('hi', HARDWAREINPUT)       # 键盘或鼠标以外的输入设备 https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-taghardwareinput
    ]

class INPUT(Structure):             # 继承 Structure 定义一个 结构体 Input https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-taginput
    _fields_ = [
        ('type', DWORD),           # type 是 ulong 类型
        ('union', Union)           # 将 union 拆分到另一个 class 中定义
    ]

def SendInput(*inputs):             # SendInput 是一个 共有体 https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendinput
    nInputs = len(inputs)           # 指定ninput 数组中元素的个数。就是插入事件的个数。
    LPINPUT = INPUT * nInputs
    pInputs = LPINPUT(*inputs)      # pInput 指向一个类型为INPUT的数组变量，该数组中的每个元素代表一个将要插入到线程事件中去的键盘或鼠标事件。
    cbSize = c_int(sizeof(INPUT))   # cbSize 指定INPUT结构的大小。如果cbSize不是INPUT结构的大小，则函数将失败返回。
    return windll.user32.SendInput(nInputs, pInputs, cbSize)

def Input(structure):                           # 通过 Union 中 存在的 变量类型 判断 鼠标 键盘 其他外设的操作
    if isinstance(structure, MOUSEINPUT):
        return INPUT(0, Union(mi=structure))
    if isinstance(structure, KEYBDINPUT):
        return INPUT(1, Union(ki=structure))
    if isinstance(structure, HARDWAREINPUT):
        return INPUT(2, Union(hi=structure))

# 鼠标事件相关
def mouse(key_number, x = 0, y = 0):
    SendInput(Input(MOUSEINPUT(x, y, 0, key_number, 0, ULONG_PTR(DWORD(0)))))

#鼠标点击 key_number 1.左 2.右 3.中 [空为左键] type 1.单击 2.双击 3.按下 4.放开 [空为单击]
def mouse_cleck(key_number = 1,type = 1):
    if key_number is 1:  # 鼠标左键
        if type is 1:
            mouse(2)
            mouse(4)
        elif type is 2:
            mouse(2)
            mouse(4)
            # 延迟
            mouse(2)
            mouse(4)
        elif type is 3:
            mouse(2)
        elif type is 4:
            mouse(4)
    elif key_number is 2:  # 鼠标右键
        if type is (1 or None):
            mouse(8)
            mouse(16)
        elif type is 2:
            mouse(8)
            mouse(16)
            # 延迟
            mouse(8)
            mouse(16)
        elif type is 3:
            mouse(8)
        elif type is 4:
            mouse(16)
    elif key_number is 3:  # 鼠标中键
        if type is (1 or None):
            mouse(32)
            mouse(64)
        elif type is 2:
            mouse(32)
            mouse(64)
            # 延迟
            mouse(32)
            mouse(64)
        elif type is 3:
            mouse(32)
        elif type is 4:
            mouse(64)
    else:
        return -1
    mouse(8)
    mouse(16)

# 鼠标相对移动
def mouse_move_re(x, y):
    mouse(1, x, y)

# 鼠标绝对移动
def mouse_move(x ,y):
    mouse(32768 | 1)        # 先将鼠标坐标移动到 0 位置 再进行移动
    mouse_move_re(x, y)

# 键盘点击
def keyboard(key_number):
    SendInput(Input(KEYBDINPUT(key_number, 0, 0, 0, ULONG_PTR(DWORD(0)))))